Avastage JavaScripti asünkroonsete generaatorite abimeetodite võimsus tõhusaks voogude loomiseks, teisendamiseks ja haldamiseks. Uurige praktilisi näiteid ja reaalseid kasutusjuhtumeid robustsete asünkroonsete rakenduste ehitamiseks.
JavaScripti asünkroonsete generaatorite abimeetodid: Voogude loomise ja haldamise meisterlikkus
Asünkroonne programmeerimine JavaScriptis on aastate jooksul oluliselt arenenud. Asünkroonsete generaatorite ja asünkroonsete iteraatorite kasutuselevõtuga said arendajad võimsad tööriistad asünkroonsete andmevoogude käsitlemiseks. Nüüd täiustavad JavaScripti asünkroonsete generaatorite abimeetodid neid võimalusi veelgi, pakkudes sujuvamat ja väljendusrikkamat viisi asünkroonsete andmevoogude loomiseks, teisendamiseks ja haldamiseks. See juhend uurib asünkroonsete generaatorite abimeetodite põhitõdesid, süveneb nende funktsioonidesse ja demonstreerib nende praktilisi rakendusi selgete näidete abil.
Asünkroonsete generaatorite ja iteraatorite mõistmine
Enne asünkroonsete generaatorite abimeetodite juurde sukeldumist on oluline mõista asünkroonsete generaatorite ja asünkroonsete iteraatorite aluseks olevaid kontseptsioone.
Asünkroonsed generaatorid
Asünkroonne generaator on funktsioon, mida saab peatada ja jätkata, andes väärtusi asünkroonselt. See võimaldab teil genereerida väärtuste jada aja jooksul, ilma et see blokeeriks põhilõime. Asünkroonsed generaatorid defineeritakse kasutades süntaksit async function*.
Näide:
async function* generateSequence(start, end) {
for (let i = start; i <= end; i++) {
await new Promise(resolve => setTimeout(resolve, 500)); // Simuleeri asünkroonset operatsiooni
yield i;
}
}
// Kasutamine
const sequence = generateSequence(1, 5);
Asünkroonsed iteraatorid
Asünkroonne iteraator on objekt, mis pakub next() meetodit, mis tagastab lubaduse (promise), mis laheneb objektiks, mis sisaldab jada järgmist väärtust ja atribuuti done, mis näitab, kas jada on ammendatud. Asünkroonseid iteraatoreid tarbitakse kasutades for await...of tsükleid.
Näide:
async function* generateSequence(start, end) {
for (let i = start; i <= end; i++) {
await new Promise(resolve => setTimeout(resolve, 500));
yield i;
}
}
async function consumeSequence() {
const sequence = generateSequence(1, 5);
for await (const value of sequence) {
console.log(value);
}
}
consumeSequence();
Asünkroonsete generaatorite abimeetodite tutvustus
Asünkroonsete generaatorite abimeetodid on meetodite kogum, mis laiendab asünkroonsete generaatorite prototüüpide funktsionaalsust. Need pakuvad mugavaid viise asünkroonsete andmevoogude manipuleerimiseks, muutes koodi loetavamaks ja hooldatavamaks. Need abimeetodid töötavad laisalt, mis tähendab, et nad töötlevad andmeid ainult siis, kui neid on vaja, mis võib parandada jõudlust.
Järgmised asünkroonsete generaatorite abimeetodid on tavaliselt saadaval (sõltuvalt JavaScripti keskkonnast ja polüfillidest):
mapfiltertakedropflatMapreducetoArrayforEach
Asünkroonsete generaatorite abimeetodite üksikasjalik uurimine
1. `map()`
Abimeetod map() teisendab iga väärtuse asünkroonses järjestuses, rakendades sellele etteantud funktsiooni. See tagastab uue asünkroonse generaatori, mis annab teisendatud väärtusi.
Süntaks:
asyncGenerator.map(callback)
Näide: Arvude voo teisendamine nende ruutudeks.
async function* generateNumbers(start, end) {
for (let i = start; i <= end; i++) {
await new Promise(resolve => setTimeout(resolve, 200));
yield i;
}
}
async function processNumbers() {
const numbers = generateNumbers(1, 5);
const squares = numbers.map(async (num) => {
await new Promise(resolve => setTimeout(resolve, 100)); // Simuleeri asünkroonset operatsiooni
return num * num;
});
for await (const square of squares) {
console.log(square);
}
}
processNumbers();
Reaalse elu kasutusjuhtum: Kujutage ette kasutajaandmete hankimist mitmest API-st ja vajadust teisendada andmed ühtsesse vormingusse. map() saab kasutada teisendusfunktsiooni rakendamiseks igale kasutajaobjektile asünkroonselt.
async function* fetchUsersFromMultipleAPIs(apiEndpoints) {
for (const endpoint of apiEndpoints) {
const response = await fetch(endpoint);
const data = await response.json();
for (const user of data) {
yield user;
}
}
}
async function processUsers() {
const apiEndpoints = [
'https://api.example.com/users1',
'https://api.example.com/users2'
];
const users = fetchUsersFromMultipleAPIs(apiEndpoints);
const normalizedUsers = users.map(async (user) => {
// Normaliseeri kasutajaandmete vorming
return {
id: user.userId || user.id,
name: user.fullName || user.name,
email: user.emailAddress || user.email
};
});
for await (const normalizedUser of normalizedUsers) {
console.log(normalizedUser);
}
}
2. `filter()`
Abimeetod filter() loob uue asünkroonse generaatori, mis annab ainult need väärtused algsest järjestusest, mis vastavad etteantud tingimusele. See võimaldab teil valikuliselt kaasata väärtusi tulemvoogu.
Süntaks:
asyncGenerator.filter(callback)
Näide: Arvude voo filtreerimine, et kaasata ainult paarisarvud.
async function* generateNumbers(start, end) {
for (let i = start; i <= end; i++) {
await new Promise(resolve => setTimeout(resolve, 200));
yield i;
}
}
async function processNumbers() {
const numbers = generateNumbers(1, 10);
const evenNumbers = numbers.filter(async (num) => {
await new Promise(resolve => setTimeout(resolve, 100));
return num % 2 === 0;
});
for await (const evenNumber of evenNumbers) {
console.log(evenNumber);
}
}
processNumbers();
Reaalse elu kasutusjuhtum: Logikirjete voo töötlemine ja kirjete filtreerimine nende raskusastme alusel. Näiteks ainult vigade ja hoiatuste töötlemine.
async function* readLogFile(filePath) {
// Simuleeri logifaili lugemist rida-realt asünkroonselt
const logEntries = [
{ timestamp: '...', level: 'INFO', message: '...' },
{ timestamp: '...', level: 'ERROR', message: '...' },
{ timestamp: '...', level: 'WARNING', message: '...' },
{ timestamp: '...', level: 'INFO', message: '...' },
{ timestamp: '...', level: 'ERROR', message: '...' }
];
for (const entry of logEntries) {
await new Promise(resolve => setTimeout(resolve, 50));
yield entry;
}
}
async function processLogs() {
const logEntries = readLogFile('path/to/log/file.log');
const errorAndWarningLogs = logEntries.filter(async (entry) => {
return entry.level === 'ERROR' || entry.level === 'WARNING';
});
for await (const log of errorAndWarningLogs) {
console.log(log);
}
}
3. `take()`
Abimeetod take() loob uue asünkroonse generaatori, mis annab ainult esimesed n väärtust algsest järjestusest. See on kasulik töödeldavate elementide arvu piiramiseks potentsiaalselt lõpmatust või väga suurest voost.
Süntaks:
asyncGenerator.take(n)
Näide: Esimese 3 arvu võtmine arvude voost.
async function* generateNumbers(start) {
let i = start;
while (true) {
await new Promise(resolve => setTimeout(resolve, 200));
yield i++;
}
}
async function processNumbers() {
const numbers = generateNumbers(1);
const firstThree = numbers.take(3);
for await (const num of firstThree) {
console.log(num);
}
}
processNumbers();
Reaalse elu kasutusjuhtum: Esimese 5 otsingutulemuse kuvamine asünkroonsest otsingu API-st.
async function* search(query) {
// Simuleeri otsingutulemuste hankimist API-st
const results = [
{ title: 'Result 1', url: '...' },
{ title: 'Result 2', url: '...' },
{ title: 'Result 3', url: '...' },
{ title: 'Result 4', url: '...' },
{ title: 'Result 5', url: '...' },
{ title: 'Result 6', url: '...' }
];
for (const result of results) {
await new Promise(resolve => setTimeout(resolve, 100));
yield result;
}
}
async function displayTopSearchResults(query) {
const searchResults = search(query);
const top5Results = searchResults.take(5);
for await (const result of top5Results) {
console.log(result);
}
}
4. `drop()`
Abimeetod drop() loob uue asünkroonse generaatori, mis jätab vahele esimesed n väärtust algsest järjestusest ja annab ülejäänud väärtused. See on vastand meetodile take() ja on kasulik voo algusosade ignoreerimiseks.
Süntaks:
asyncGenerator.drop(n)
Näide: Esimese 2 arvu väljajätmine arvude voost.
async function* generateNumbers(start, end) {
for (let i = start; i <= end; i++) {
await new Promise(resolve => setTimeout(resolve, 200));
yield i;
}
}
async function processNumbers() {
const numbers = generateNumbers(1, 5);
const remainingNumbers = numbers.drop(2);
for await (const num of remainingNumbers) {
console.log(num);
}
}
processNumbers();
Reaalse elu kasutusjuhtum: Suure andmehulga lehitsemine API-st, jättes vahele juba kuvatud tulemused.
async function* fetchData(url, pageSize, pageNumber) {
const offset = (pageNumber - 1) * pageSize;
// Simuleeri andmete hankimist nihkega
const data = [
{ id: 1, name: 'Item 1' },
{ id: 2, name: 'Item 2' },
{ id: 3, name: 'Item 3' },
{ id: 4, name: 'Item 4' },
{ id: 5, name: 'Item 5' },
{ id: 6, name: 'Item 6' },
{ id: 7, name: 'Item 7' },
{ id: 8, name: 'Item 8' }
];
const pageData = data.slice(offset, offset + pageSize);
for (const item of pageData) {
await new Promise(resolve => setTimeout(resolve, 100));
yield item;
}
}
async function displayPage(pageNumber) {
const pageSize = 3;
const allData = fetchData('api/data', pageSize, pageNumber);
const page = allData.drop((pageNumber - 1) * pageSize); // jäta eelmiste lehtede elemendid vahele
const results = page.take(pageSize);
for await (const item of results) {
console.log(item);
}
}
// Näide kasutamisest
displayPage(2);
5. `flatMap()`
Abimeetod flatMap() teisendab iga väärtuse asünkroonses järjestuses, rakendades funktsiooni, mis tagastab asünkroonse itereeritava (Async Iterable). Seejärel lamedab see tulemuseks oleva asünkroonse itereeritava üheks asünkroonseks generaatoriks. See on kasulik iga väärtuse teisendamiseks väärtuste vooks ja seejärel nende voogude ühendamiseks.
Süntaks:
asyncGenerator.flatMap(callback)
Näide: Lausete voo teisendamine sõnade vooks.
async function* generateSentences() {
const sentences = [
'This is the first sentence.',
'This is the second sentence.',
'This is the third sentence.'
];
for (const sentence of sentences) {
await new Promise(resolve => setTimeout(resolve, 200));
yield sentence;
}
}
async function* stringToWords(sentence) {
const words = sentence.split(' ');
for (const word of words) {
await new Promise(resolve => setTimeout(resolve, 50));
yield word;
}
}
async function processSentences() {
const sentences = generateSentences();
const words = sentences.flatMap(async (sentence) => {
return stringToWords(sentence);
});
for await (const word of words) {
console.log(word);
}
}
processSentences();
Reaalse elu kasutusjuhtum: Mitme blogipostituse kommentaaride hankimine ja nende ühendamine üheks vooks töötlemiseks.
async function* fetchBlogPostIds() {
const blogPostIds = [1, 2, 3]; // Simuleeri blogipostituste ID-de hankimist API-st
for (const id of blogPostIds) {
await new Promise(resolve => setTimeout(resolve, 100));
yield id;
}
}
async function* fetchCommentsForPost(postId) {
// Simuleeri blogipostituse kommentaaride hankimist API-st
const comments = [
{ postId: postId, text: `Comment 1 for post ${postId}` },
{ postId: postId, text: `Comment 2 for post ${postId}` }
];
for (const comment of comments) {
await new Promise(resolve => setTimeout(resolve, 50));
yield comment;
}
}
async function processComments() {
const postIds = fetchBlogPostIds();
const allComments = postIds.flatMap(async (postId) => {
return fetchCommentsForPost(postId);
});
for await (const comment of allComments) {
console.log(comment);
}
}
6. `reduce()`
Abimeetod reduce() rakendab funktsiooni akumulaatori ja iga asünkroonse generaatori väärtuse vastu (vasakult paremale), et redutseerida see üheks väärtuseks. See on kasulik andmete koondamiseks asünkroonsest voost.
Süntaks:
asyncGenerator.reduce(callback, initialValue)
Näide: Arvude summa arvutamine voos.
async function* generateNumbers(start, end) {
for (let i = start; i <= end; i++) {
await new Promise(resolve => setTimeout(resolve, 200));
yield i;
}
}
async function processNumbers() {
const numbers = generateNumbers(1, 5);
const sum = await numbers.reduce(async (accumulator, num) => {
await new Promise(resolve => setTimeout(resolve, 100));
return accumulator + num;
}, 0);
console.log('Sum:', sum);
}
processNumbers();
Reaalse elu kasutusjuhtum: API-kutsete seeria keskmise vastamisaja arvutamine.
async function* fetchResponseTimes(apiEndpoints) {
for (const endpoint of apiEndpoints) {
const startTime = Date.now();
try {
await fetch(endpoint);
const endTime = Date.now();
const responseTime = endTime - startTime;
await new Promise(resolve => setTimeout(resolve, 50));
yield responseTime;
} catch (error) {
console.error(`Error fetching ${endpoint}: ${error}`);
yield 0; // Või käsitle viga sobivalt
}
}
}
async function calculateAverageResponseTime() {
const apiEndpoints = [
'https://api.example.com/endpoint1',
'https://api.example.com/endpoint2',
'https://api.example.com/endpoint3'
];
const responseTimes = fetchResponseTimes(apiEndpoints);
let count = 0;
const sum = await responseTimes.reduce(async (accumulator, time) => {
count++;
return accumulator + time;
}, 0);
const average = count > 0 ? sum / count : 0;
console.log(`Average response time: ${average} ms`);
}
7. `toArray()`
Abimeetod toArray() tarbib asünkroonse generaatori ja tagastab lubaduse (promise), mis laheneb massiiviks, mis sisaldab kõiki generaatori poolt antud väärtusi. See on kasulik, kui teil on vaja koguda kõik väärtused voost ühte massiivi edasiseks töötlemiseks.
Süntaks:
asyncGenerator.toArray()
Näide: Arvude kogumine voost massiivi.
async function* generateNumbers(start, end) {
for (let i = start; i <= end; i++) {
await new Promise(resolve => setTimeout(resolve, 200));
yield i;
}
}
async function processNumbers() {
const numbers = generateNumbers(1, 5);
const numberArray = await numbers.toArray();
console.log('Number Array:', numberArray);
}
processNumbers();
Reaalse elu kasutusjuhtum: Kõikide elementide kogumine lehekülgedega API-st ühte massiivi kliendipoolseks filtreerimiseks või sorteerimiseks.
async function* fetchAllItems(apiEndpoint) {
let pageNumber = 1;
const pageSize = 100; // Kohanda vastavalt API lehitsemise piirangutele
while (true) {
const url = `${apiEndpoint}?page=${pageNumber}&pageSize=${pageSize}`;
const response = await fetch(url);
const data = await response.json();
if (!data || data.length === 0) {
break; // Rohkem andmeid pole
}
for (const item of data) {
await new Promise(resolve => setTimeout(resolve, 50));
yield item;
}
pageNumber++;
}
}
async function processAllItems() {
const apiEndpoint = 'https://api.example.com/items';
const allItems = fetchAllItems(apiEndpoint);
const itemsArray = await allItems.toArray();
console.log(`Fetched ${itemsArray.length} items.`);
// Edasist töötlemist saab teostada `itemsArray` peal
}
8. `forEach()`
Abimeetod forEach() täidab etteantud funktsiooni üks kord iga väärtuse kohta asünkroonses generaatoris. Erinevalt teistest abimeetoditest ei tagasta forEach() uut asünkroonset generaatorit; seda kasutatakse kõrvalmõjude teostamiseks iga väärtuse puhul.
Süntaks:
asyncGenerator.forEach(callback)
Näide: Iga arvu logimine voost konsooli.
async function* generateNumbers(start, end) {
for (let i = start; i <= end; i++) {
await new Promise(resolve => setTimeout(resolve, 200));
yield i;
}
}
async function processNumbers() {
const numbers = generateNumbers(1, 5);
await numbers.forEach(async (num) => {
await new Promise(resolve => setTimeout(resolve, 100));
console.log('Number:', num);
});
}
processNumbers();
Reaalse elu kasutusjuhtum: Reaalajas uuenduste saatmine kasutajaliidesele, kui andmeid voost töödeldakse.
async function* fetchRealTimeData(dataSource) {
//Simuleeri reaalajas andmete hankimist (nt aktsiahinnad).
const dataStream = [
{ timestamp: new Date(), price: 100 },
{ timestamp: new Date(), price: 101 },
{ timestamp: new Date(), price: 102 }
];
for (const dataPoint of dataStream) {
await new Promise(resolve => setTimeout(resolve, 500));
yield dataPoint;
}
}
async function updateUI() {
const realTimeData = fetchRealTimeData('stock-api');
await realTimeData.forEach(async (data) => {
//Simuleeri kasutajaliidese uuendamist
await new Promise(resolve => setTimeout(resolve, 100));
console.log(`Updating UI with data: ${JSON.stringify(data)}`);
// Kood tegelikuks kasutajaliidese uuendamiseks läheks siia.
});
}
Asünkroonsete generaatorite abimeetodite kombineerimine keerukate andmetorude jaoks
Asünkroonsete generaatorite abimeetodite tegelik võimsus tuleneb nende võimest neid omavahel aheldada, et luua keerukaid andmetorusid. See võimaldab teil teostada mitmeid teisendusi ja operatsioone asünkroonsel vool lühidalt ja loetavalt.
Näide: Arvude voo filtreerimine, et kaasata ainult paarisarvud, seejärel nende ruutu tõstmine ja lõpuks esimese 3 tulemuse võtmine.
async function* generateNumbers(start) {
let i = start;
while (true) {
await new Promise(resolve => setTimeout(resolve, 100));
yield i++;
}
}
async function processNumbers() {
const numbers = generateNumbers(1);
const processedNumbers = numbers
.filter(async (num) => num % 2 === 0)
.map(async (num) => num * num)
.take(3);
for await (const num of processedNumbers) {
console.log(num);
}
}
processNumbers();
Reaalse elu kasutusjuhtum: Kasutajaandmete hankimine, kasutajate filtreerimine nende asukoha alusel, nende andmete teisendamine, et kaasata ainult asjakohased väljad, ja seejärel esimese 10 kasutaja kuvamine kaardil.
async function* fetchUsers() {
// Simuleeri kasutajate hankimist andmebaasist või API-st
const users = [
{ id: 1, name: 'John Doe', location: 'New York', email: 'john.doe@example.com' },
{ id: 2, name: 'Jane Smith', location: 'London', email: 'jane.smith@example.com' },
{ id: 3, name: 'Ken Tan', location: 'Singapore', email: 'ken.tan@example.com' },
{ id: 4, name: 'Alice Jones', location: 'New York', email: 'alice.jones@example.com' },
{ id: 5, name: 'Bob Williams', location: 'London', email: 'bob.williams@example.com' },
{ id: 6, name: 'Siti Rahman', location: 'Singapore', email: 'siti.rahman@example.com' },
{ id: 7, name: 'Ahmed Khan', location: 'Dubai', email: 'ahmed.khan@example.com' },
{ id: 8, name: 'Maria Garcia', location: 'Madrid', email: 'maria.garcia@example.com' },
{ id: 9, name: 'Li Wei', location: 'Shanghai', email: 'li.wei@example.com' },
{ id: 10, name: 'Hans Müller', location: 'Berlin', email: 'hans.muller@example.com' },
{ id: 11, name: 'Emily Chen', location: 'Sydney', email: 'emily.chen@example.com' }
];
for (const user of users) {
await new Promise(resolve => setTimeout(resolve, 50));
yield user;
}
}
async function displayUsersOnMap(location, maxUsers) {
const users = fetchUsers();
const usersForMap = users
.filter(async (user) => user.location === location)
.map(async (user) => ({
id: user.id,
name: user.name,
location: user.location
}))
.take(maxUsers);
console.log(`Displaying up to ${maxUsers} users from ${location} on the map:`);
for await (const user of usersForMap) {
console.log(user);
}
}
// Kasutusnäited:
displayUsersOnMap('New York', 2);
displayUsersOnMap('London', 5);
Polüfillid ja brauseri tugi
Asünkroonsete generaatorite abimeetodite tugi võib varieeruda sõltuvalt JavaScripti keskkonnast. Kui peate toetama vanemaid brausereid või keskkondi, peate võib-olla kasutama polüfille. Polüfill pakub puuduvat funktsionaalsust, implementeerides selle JavaScriptis. Asünkroonsete generaatorite abimeetodite jaoks on saadaval mitu polüfillide teeki, näiteks core-js.
Näide kasutades core-js:
// Impordi vajalikud polüfillid
require('core-js/features/async-iterator/map');
require('core-js/features/async-iterator/filter');
// ... impordi teised vajalikud abimeetodid
Vigade käsitlemine
Asünkroonsete operatsioonidega töötamisel on vigade korrektne käsitlemine ülioluline. Asünkroonsete generaatorite abimeetoditega saab vigade käsitlemist teha kasutades try...catch plokke asünkroonsetes funktsioonides, mida abimeetodites kasutatakse.
Näide: Vigade käsitlemine andmete hankimisel map() operatsiooni sees.
async function* fetchData(urls) {
for (const url of urls) {
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
yield data;
} catch (error) {
console.error(`Error fetching data from ${url}: ${error}`);
yield null; // Või käsitle viga sobivalt, nt andes veaobjekti
}
}
}
async function processData() {
const urls = [
'https://api.example.com/data1',
'https://api.example.com/data2',
'https://api.example.com/data3'
];
const dataStream = fetchData(urls);
const processedData = dataStream.map(async (data) => {
if (data === null) {
return null; // Levita viga edasi
}
// Töötle andmeid
return data;
});
for await (const item of processedData) {
if (item === null) {
console.log('Skipping item due to error');
continue;
}
console.log('Processed Item:', item);
}
}
processData();
Parimad tavad ja kaalutlused
- Laisk hindamine: Asünkroonsete generaatorite abimeetodeid hinnatakse laisalt, mis tähendab, et nad töötlevad andmeid ainult siis, kui neid küsitakse. See võib parandada jõudlust, eriti suurte andmekogumitega tegelemisel.
- Vigade käsitlemine: Käsitsege vigu alati korrektselt abimeetodites kasutatavate asünkroonsete funktsioonide sees.
- Polüfillid: Kasutage vajadusel polüfille vanemate brauserite või keskkondade toetamiseks.
- Loetavus: Kasutage kirjeldavaid muutujate nimesid ja kommentaare, et muuta oma kood loetavamaks ja hooldatavamaks.
- Jõudlus: Olge teadlik mitme abimeetodi aheldamise jõudlusmõjudest. Kuigi laiskus aitab, võib liigne aheldamine siiski lisada üldkulusid.
Kokkuvõte
JavaScripti asünkroonsete generaatorite abimeetodid pakuvad võimsat ja elegantset viisi asünkroonsete andmevoogude loomiseks, teisendamiseks ja haldamiseks. Neid abimeetodeid kasutades saavad arendajad kirjutada lühemat, loetavamat ja hooldatavamat koodi keerukate asünkroonsete operatsioonide käsitlemiseks. Asünkroonsete generaatorite ja iteraatorite põhitõdede ning iga abimeetodi funktsionaalsuse mõistmine on nende tööriistade tõhusaks kasutamiseks reaalsetes rakendustes hädavajalik. Olenemata sellest, kas ehitate andmetorusid, töötlete reaalajas andmeid või käsitlete asünkroonseid API vastuseid, võivad asünkroonsete generaatorite abimeetodid teie koodi oluliselt lihtsustada ja selle üldist tõhusust parandada.